package work.mediway.mdm.core.exception;

import com.alibaba.fastjson.JSON;
import com.mediway.hos.base.model.BaseResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.nio.file.AccessDeniedException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import work.mediway.mdm.core.enums.CodeAndMessageEnum;
import work.mediway.mdm.core.util.HttpContextUtil;

/**
 * 全局异常捕获<br>
 *
 * @author zhongyj
 * @date 2019/7/1
 */
@Slf4j
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
@RestControllerAdvice(basePackages = {"work"})
public class MdmGlobalExceptionHandler {
    /**
     * BizException
     *
     * @param e BizException
     * @return ResponseDTO
     */
    @ExceptionHandler(value = BizException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse<String> handleBizException(BizException e) {
        this.buildErrorInfo("业务异常", e);
        return BaseResponse.error(e.getMessage());
    }

    @ExceptionHandler(value = DataException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse<String> handleDataException(DataException e) {
        this.buildErrorInfo("数据异常", e.getMessage());
        return BaseResponse.error(e.getMessage());
    }

    /**
     * AccessDeniedException
     *
     * @return ResponseDTO
     */
    @ExceptionHandler(value = AccessDeniedException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse<String> handleAccessDeniedException(AccessDeniedException e) {
        this.buildErrorInfo(CodeAndMessageEnum.FILE_READING_ERROR.getMessage(), e);
        return BaseResponse.error(CodeAndMessageEnum.FILE_READING_ERROR);
    }

    /**
     * 接口请求方式错误
     *
     * @param e HttpRequestMethodNotSupportedException
     * @return ResponseDTO
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse<String> excelDataConvertException(HttpRequestMethodNotSupportedException e) {
        this.buildErrorInfo("接口请求方式错误", e);
        return BaseResponse.error(CodeAndMessageEnum.METHOD_NOT_ALLOWED.getCode(), e.getMessage());
    }

    /**
     * 统一处理请求参数校验(普通传参)
     *
     * @param e ConstraintViolationException
     * @return ResponseDTO
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse<String> handleConstraintViolationException(ConstraintViolationException e) {
        StringBuilder message = new StringBuilder();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
            Path path = violation.getPropertyPath();
            String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), ".");
            message.append(" 【").append(pathArr[1]).append("】 ").append(violation.getMessage()).append(", ");
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        this.buildErrorInfo("参数校验失败", e.getMessage());
        return BaseResponse.error(CodeAndMessageEnum.REQUEST_PARAM_NULL.getCode(), message.toString());
    }

    /**
     * 统一处理请求参数校验(body传参)
     *
     * @param e ConstraintViolationException
     * @return ResponseDTO
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        StringBuilder message = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            message.append(" 【").append(error.getField()).append("】 ").append(error.getDefaultMessage()).append(", ");
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        this.buildErrorInfo("参数校验失败", null);
        return BaseResponse.error(message.toString());
    }

    /**
     * 统一处理请求参数校验(实体对象传参)
     *
     * @param e BindException
     * @return ResponseDTO
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public BaseResponse<String> handleBindException(BindException e) {
        StringBuilder message = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
            message.append(" 【").append(error.getField()).append("】 ").append(error.getDefaultMessage()).append(", ");
        }
        message = new StringBuilder(message.substring(0, message.length() - 1));
        this.buildErrorInfo("参数校验失败", null);
        return BaseResponse.error(message.toString());
    }

    protected void buildErrorInfo(String msg, Object e) {
        //获取所有参数的map集合
        HttpServletRequest request = HttpContextUtil.getRequest();
        Map<String, String[]> parameterMap = request.getParameterMap();
        String param = JSON.toJSONString(parameterMap);
        String uri = request.getRequestURI();
        log.error(StrUtil.format("\n=================================== {} ===============================\n" +
                        "【 参数: {} 】\n" +
                        "【 URI: {} 】\n" +
                        "【 请求时间: {} 】\n" +
                        "【 异常信息: {} 】\n" +
                        "=========================================================================="
                , msg, param, uri, DateUtil.now(), e));
    }

}













